#include <stdio.h>
#include <string.h>

#include <glib.h>
#include <oci.h>

#include "gkserver.h"

#define ARRAY_SIZE 10

extern gboolean       verbose, debug;

static OCIError       *errhp = (OCIError *)0;
static OCIEnv         *envhp;
static OCISvcCtx      *svchp = (OCISvcCtx *)0;
static OCIServer      *svrhp = (OCIServer *)0;
static OCISession     *seshp = (OCISession *)0;
static OCIStmt        *cpu_sth = (OCIStmt *)0, *mem_sth = (OCIStmt *)0, *prc_sth = (OCIStmt *)0;

static cpu_cnt=0, prc_cnt=0, mem_cnt=0;

static gint      prc_host_id[ARRAY_SIZE], processes[ARRAY_SIZE], running[ARRAY_SIZE], forks[ARRAY_SIZE], users[ARRAY_SIZE];
static gint      load[ARRAY_SIZE];



static sword
oci_error(OCIError *ehp, sword ostat) {
  gchar           errbuf[1024];
  sword           errcode = (sb4) 0;

  if(ostat != OCI_SUCCESS) {

    switch (ostat) {

    case OCI_SUCCESS_WITH_INFO:

      OCIErrorGet((dvoid *)ehp, (ub4)1, (text *)NULL, &errcode, errbuf, (ub4) sizeof(errbuf), (ub4)OCI_HTYPE_ERROR);
      fprintf(stderr, "OCI_SUCCESS_WITH_INTO: %s\n", errbuf);
      break;


    case OCI_ERROR:

      OCIErrorGet((dvoid *)ehp, (ub4)1, (text *)NULL, &errcode, errbuf, (ub4) sizeof(errbuf), (ub4)OCI_HTYPE_ERROR);
      fprintf(stderr, "OCI_ERROR: %s\n", errbuf);
      break;

    case OCI_INVALID_HANDLE:

      fprintf(stderr, "Error: OCI_INVALID_HANDLE");
      break;

    case OCI_NEED_DATA:
    case OCI_STILL_EXECUTING:
    case OCI_CONTINUE:
    case OCI_NO_DATA:
      break;

    default:
      break;
    }

  }
 return(ostat);
}


sword oci_bind_string(OCIStmt *st, OCIError *ehp, ub4 pos, const gchar *value) {
  OCIBind      *bhp = (OCIBind *)0;

  return(OCIBindByPos((OCIStmt *)st, 
		      (OCIBind **)&bhp, 
		      (OCIError *)ehp, 
		      (ub4)pos, 
		      (dvoid *)value, (sb4)strlen(value)+1, 
		      (ub2)SQLT_STR, 
		      (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, 
		      (ub4)OCI_DEFAULT)
	 
	 );
}



sword oci_bind_int(OCIStmt *st, OCIError *ehp, ub4 pos, gint *value, sb4 size) {
  OCIBind      *bhp = (OCIBind *)0;

  return(OCIBindByPos((OCIStmt *)st, 
		      (OCIBind **)&bhp, 
		      (OCIError *)ehp, 
		      (ub4)pos, 
		      (dvoid *)value, (sb4)size,
		      (ub2)SQLT_INT, 
		      (dvoid *)0, (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, 
		      (ub4)OCI_DEFAULT)
	 
	 );
}




sword
oci_insert_cpu(GKServer *gkserver, const gchar *gkline) {
  gint64    user, nice, sys, idle, total;
  gint      cpu_num;


  sscanf(gkline, "%d %llu %llu %llu %llu", &cpu_num, &user, &nice, &sys, &idle);
  total = user + idle + sys + idle;

  if(debug)
    fprintf(stderr, "CPU - %-24s(%d) : #%d %llu %llu %llu %llu\n",
	    gkserver->hostname, gkserver->db_host_id, cpu_num, user*100/total, nice*100/total, sys*100/total, idle*100/total );

  cpu_cnt++;

  return(OCI_SUCCESS);
}



sword
oci_insert_mem(GKServer *gkserver,
	       const gchar *gkline)
{
  gint64     total, used, free, shared, buffers, cached;

  sscanf(gkline, "%llu %llu %llu %llu %llu %llu", &total, &used, &free, &shared, &buffers, &cached);
  
  if(debug)
    fprintf(stderr, "MEM - %-24s(%d) : %llu %llu %llu\n",
	    gkserver->hostname, gkserver->db_host_id, total/1048576, used/1048576, free/1048576);

  mem_cnt++;

  return(OCI_SUCCESS);
}



sword
oci_insert_proc(GKServer *gkserver, const gchar *gkline) {

  sscanf(gkline, "%d %d %lu %d %d", &processes[prc_cnt], &running[prc_cnt], &forks[prc_cnt], &load[prc_cnt], &users[prc_cnt] );
  prc_host_id[prc_cnt] = gkserver->db_host_id;

  if(debug)
    fprintf(stderr, "PRC - %-24s(%d) : %d %d %d %d\n", 
	    gkserver->hostname, gkserver->db_host_id, processes[prc_cnt], running[prc_cnt], load[prc_cnt], users[prc_cnt] );

  if(prc_cnt == ARRAY_SIZE - 1) {

    fprintf(stderr, ">>>>>>>>>>>>>>>>>>> INSERT GKR_PROC\n");
    oci_error(errhp, OCIStmtExecute(svchp, prc_sth, errhp, (ub4)prc_cnt+1, (ub4)0, (OCISnapshot *)0, (OCISnapshot *)0, (ub4)OCI_DEFAULT));
    OCITransCommit(svchp, errhp, (ub4) OCI_DEFAULT);
    prc_cnt=0;
  }
  else
    prc_cnt++;

  return(OCI_SUCCESS);
}




sword
oci_merge_host(GKServer *gkserver)
{
  OCIStmt       *sth = (OCIStmt *)0;
  OCIDefine     *dhp = (OCIDefine *)0;
  sb2           indp=0;
  sword         ret;

  gchar *ssql = "select host_id from gkr_host where hostname = :p1";
  gchar *isql = "BEGIN insert into gkr_host (host_id, hostname, sysname, gkrellmd_version, n_cpu) values (gkr_host_id.nextval, :p2, :p3, :p4, :p5)\
                 RETURNING host_id INTO :host_id; END;";
  gchar *usql = "update gkr_host set sysname=:p1, gkrellmd_version=:p2, n_cpu=:p3, last_update=sysdate where host_id = :p4";

  if(debug)
    fprintf(stderr, "Updating GKR_HOST table for host '%s'\n", gkserver->hostname);


  OCIHandleAlloc((dvoid *)envhp, (dvoid **) &sth, (ub4)OCI_HTYPE_STMT, (size_t)0, (dvoid **)0);
  oci_error(errhp, OCIStmtPrepare(sth, errhp, (text *)ssql, (ub4) strlen(ssql), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT));
  oci_bind_string(sth, errhp, 1, gkserver->hostname);

  OCIDefineByPos((OCIStmt *) sth,
		 (OCIDefine **) &dhp,
		 (OCIError *)errhp, 
		 (ub4) 1,
		 (dvoid *)&gkserver->db_host_id, sizeof(gkserver->db_host_id), 
		 (ub2)SQLT_INT, 
		 (dvoid *)&indp, (ub2 *)0, (ub2 *)0, 
		 (ub4)OCI_DEFAULT);
  if( (ret = oci_error(errhp, OCIStmtExecute(svchp, sth, errhp, (ub4)1, (ub4)0, (OCISnapshot *)0, (OCISnapshot *)0, (ub4)OCI_DEFAULT))) == OCI_NO_DATA) {

    if(debug)
      fprintf(stderr, "  host not found in database, creating\n");

    oci_error(errhp, OCIStmtPrepare(sth, errhp, (text *)isql, (ub4) strlen(isql), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT));
    oci_bind_string(sth, errhp, 1, gkserver->hostname);
    oci_bind_string(sth, errhp, 2, gkserver->sysname);
    oci_bind_string(sth, errhp, 3, gkserver->gkversion);
    oci_bind_int(sth, errhp, 4, &gkserver->ncpu, sizeof(gkserver->ncpu) );
    oci_bind_int(sth, errhp, 5, &gkserver->db_host_id, sizeof(gkserver->db_host_id) );
    oci_error(errhp, OCIStmtExecute(svchp, sth, errhp, (ub4)1, (ub4)0, (OCISnapshot *)0, (OCISnapshot *)0, (ub4)OCI_DEFAULT));
    OCITransCommit (svchp, errhp, (ub4) OCI_DEFAULT);
  }

  if( ret == OCI_SUCCESS) {
    
    if(debug)
      fprintf(stderr, "  updating host_id '%d' in database\n", gkserver->db_host_id);

    oci_error(errhp, OCIStmtPrepare(sth, errhp, (text *)usql, (ub4) strlen(usql), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT));
    oci_bind_string(sth, errhp, 1, gkserver->sysname);
    oci_bind_string(sth, errhp, 2, gkserver->gkversion);
    oci_bind_int(sth, errhp, 3, &gkserver->ncpu, sizeof(gkserver->ncpu) );
    oci_bind_int(sth, errhp, 4, &gkserver->db_host_id, sizeof(gkserver->db_host_id) );
    oci_error(errhp, OCIStmtExecute(svchp, sth, errhp, (ub4)1, (ub4)0, (OCISnapshot *)0, (OCISnapshot *)0, (ub4)OCI_DEFAULT));
    OCITransCommit (svchp, errhp, (ub4) OCI_DEFAULT);
  }

  if(debug)
    fprintf(stderr, "Done updating '%s'\n\n", gkserver->hostname);

  OCIHandleFree(sth, OCI_HTYPE_STMT);
  return(OCI_SUCCESS);
}




sword
oci_connect(const gchar *user,
	    const gchar *pass,
	    const gchar *sid,
	    const ub4 mode) 
{
  gchar   *cpu_sql = "insert into gkr_cpu (host_id, cpu_id, user, sys, idle) values (:p1, :p2, :p3, :p4)";
  gchar   *mem_sql = "insert into gkr_mem (host_id, total, used, free, swap_in, swap_out) values (:p1, :p2, :p3, :p4, :p5, :p6)";
  gchar   *prc_sql = "insert into gkr_proc (host_id, n_processes, n_running, load, n_users, ts) values (:p1, :p2, :p3, :p4, :p5, sysdate)";
  gchar   vbuf[512];
  
  OCIEnvCreate(&envhp, (ub4)OCI_DEFAULT, (dvoid *)0, (dvoid *)0, (dvoid *)0, (dvoid *)0, (size_t)0, (dvoid **)0);
  OCIHandleAlloc((dvoid *)envhp, (dvoid **)&errhp, (ub4)OCI_HTYPE_ERROR, (size_t)0, (dvoid **)0);
  OCIHandleAlloc((dvoid *)envhp, (dvoid **)&svrhp, (ub4)OCI_HTYPE_SERVER, (size_t)0, (dvoid **)0);

  if(oci_error(errhp, OCIServerAttach(svrhp, errhp, (text *)sid, (sb4)strlen(sid), (ub4)OCI_DEFAULT)))
    return(OCI_ERROR);
  

  OCIHandleAlloc((dvoid *)envhp, (dvoid **) &svchp, (ub4)OCI_HTYPE_SVCCTX, (size_t)0, (dvoid **)0);
  OCIAttrSet((dvoid *)svchp, (ub4)OCI_HTYPE_SVCCTX, (dvoid *)svrhp, (ub4)0, (ub4)OCI_ATTR_SERVER, errhp);

  OCIHandleAlloc((dvoid *)envhp, (dvoid **) &seshp, (ub4)OCI_HTYPE_SESSION, (size_t)0, (dvoid **)0);
  OCIAttrSet((dvoid *)seshp, (ub4)OCI_HTYPE_SESSION, (dvoid *)user, (ub4)strlen(user), (ub4)OCI_ATTR_USERNAME, errhp);
  OCIAttrSet((dvoid *)seshp, (ub4)OCI_HTYPE_SESSION, (dvoid *)pass, (ub4)strlen(pass), (ub4)OCI_ATTR_PASSWORD, errhp);

  if(oci_error(errhp, OCISessionBegin(svchp, errhp, seshp, (ub4)OCI_CRED_RDBMS, (ub4)OCI_DEFAULT))) 
    return(OCI_ERROR);
  
  OCIAttrSet((dvoid *)svchp, (ub4)OCI_HTYPE_SVCCTX, (dvoid *)seshp, (ub4)0, (ub4)OCI_ATTR_SESSION, errhp);

  OCIServerVersion(svchp, errhp, (text *)vbuf, (ub4)sizeof(vbuf), (ub1)OCI_HTYPE_SVCCTX);
  
  if(verbose)
    printf("%s\n\n", vbuf);

  OCIHandleAlloc((dvoid *)envhp, (dvoid **) &cpu_sth, (ub4)OCI_HTYPE_STMT, (size_t)0, (dvoid **)0);
  OCIHandleAlloc((dvoid *)envhp, (dvoid **) &mem_sth, (ub4)OCI_HTYPE_STMT, (size_t)0, (dvoid **)0);
  OCIHandleAlloc((dvoid *)envhp, (dvoid **) &prc_sth, (ub4)OCI_HTYPE_STMT, (size_t)0, (dvoid **)0);

  oci_error(errhp, OCIStmtPrepare(cpu_sth, errhp, (text *)cpu_sql, (ub4) strlen(cpu_sql), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT));
  oci_error(errhp, OCIStmtPrepare(mem_sth, errhp, (text *)mem_sql, (ub4) strlen(mem_sql), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT));

  oci_error(errhp, OCIStmtPrepare(prc_sth, errhp, (text *)prc_sql, (ub4) strlen(prc_sql), (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT));
  oci_bind_int(prc_sth, errhp, 1, &prc_host_id[0], sizeof(prc_host_id[0]) );
  oci_bind_int(prc_sth, errhp, 2, &processes[0], sizeof(processes[0]) );
  oci_bind_int(prc_sth, errhp, 3, &running[0], sizeof(running[0]) );
  oci_bind_int(prc_sth, errhp, 4, &load[0], sizeof(load[0]) );
  oci_bind_int(prc_sth, errhp, 5, &users[0], sizeof(users[0]) );


  return(OCI_SUCCESS);
}

